package com.easylinkin.linkappapi.openapi.service;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.mapper.DeviceMapper;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.deviceattribute.constant.PhysicalModelTypeConstant;
import com.easylinkin.linkappapi.deviceattribute.entity.DeviceAttribute;
import com.easylinkin.linkappapi.deviceattribute.entity.DeviceAttributeTenantConfig;
import com.easylinkin.linkappapi.deviceattribute.service.DeviceAttributeService;
import com.easylinkin.linkappapi.deviceattribute.service.DeviceAttributeTenantConfigService;
import com.easylinkin.linkappapi.deviceparm.entity.DeviceParm;
import com.easylinkin.linkappapi.deviceparm.mapper.DeviceParmMapper;
import com.easylinkin.linkappapi.deviceparm.service.DeviceParmService;
import com.easylinkin.linkappapi.deviceservice.entity.DeviceServiceTenantConfig;
import com.easylinkin.linkappapi.deviceservice.entity.DeviceServices;
import com.easylinkin.linkappapi.deviceservice.service.DeviceServiceService;
import com.easylinkin.linkappapi.deviceservice.service.DeviceServiceTenantConfigService;
import com.easylinkin.linkappapi.devicetype.entity.DeviceType;
import com.easylinkin.linkappapi.devicetype.service.DeviceTypeService;
import com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit;
import com.easylinkin.linkappapi.deviceunit.mapper.DeviceUnitMapper;
import com.easylinkin.linkappapi.deviceunit.service.DeviceUnitService;
import com.easylinkin.linkappapi.openapi.dto.DataType;
import com.easylinkin.linkappapi.openapi.dto.DeviceByProject;
import com.easylinkin.linkappapi.openapi.dto.DeviceVersion;
import com.easylinkin.linkappapi.openapi.dto.PhysicsModel;
import com.easylinkin.linkappapi.openapi.dto.PropertiesModel;
import com.easylinkin.linkappapi.openapi.dto.ServicesModel;
import com.easylinkin.linkappapi.openapi.event.DeviceUnitVersionUpdateEvent;
import com.easylinkin.linkappapi.powerdistribution.entity.DistributionCabinetConfigurationExpression;
import com.easylinkin.linkappapi.powerdistribution.entity.DistributionCabinetTypeSite;
import com.easylinkin.linkappapi.powerdistribution.service.DistributionCabinetConfigurationExpressionService;
import com.easylinkin.linkappapi.powerdistribution.service.DistributionCabinetTypeSiteService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.security.entity.LinkappUser;
import com.easylinkin.linkappapi.security.mapper.LinkappUserMapper;
import com.easylinkin.linkappapi.security.service.LinkappUserService;
import com.easylinkin.linkappapi.space.entity.LinkappArea;
import com.easylinkin.linkappapi.space.entity.LinkappSpace;
import com.easylinkin.linkappapi.space.mapper.LinkappAreaMapper;
import com.easylinkin.linkappapi.space.mapper.LinkappSpaceMapper;
import com.easylinkin.linkappapi.space.service.LinkappAreaService;
import com.easylinkin.linkappapi.space.vo.SpaceTreeVo;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.entity.TenantRefDeviceUnit;
import com.easylinkin.linkappapi.tenant.mapper.LinkappTenantMapper;
import com.easylinkin.linkappapi.tenant.mapper.TenantRefDeviceUnitMapper;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;

import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.ServletRequestAttributes;
import site.morn.framework.context.LoginValidator;
import site.morn.rest.RestBuilders;
import site.morn.rest.RestMessage;
import site.morn.util.BeanFunctionUtils;

@Service
public class SynchronizationLinkthingService {

    @Value("${linkapp.openReceiving:true}")
    private Boolean openReceiving;
    @Autowired
    private DeviceUnitService deviceUnitService;
    @Autowired
    private DeviceTypeService deviceTypeService;
    @Autowired
    private DeviceParmService deviceParmService;
    @Autowired
    private DeviceParmMapper deviceParmMapper;
    @Autowired
    private DeviceAttributeService deviceAttributeService;
    @Autowired
    private DeviceServiceService deviceServiceService;
    @Resource
    private LinkappTenantService tenantService;
    @Resource
    private LinkappUserService linkappUserService;
    @Resource
    private LinkappUserMapper linkappUserMapper;
    @Resource
    private DeviceService deviceService;
    @Resource
    private OpenApiService openApiService;
    @Resource
    private DeviceMapper deviceMapper;
    @Resource
    private DeviceUnitMapper deviceUnitMapper;
    @Resource
    private TenantRefDeviceUnitMapper tenantRefDeviceUnitMapper;
    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private LinkappTenantMapper tenantMapper;
    @Resource
    private DistributionCabinetConfigurationExpressionService distributionCabinetConfigurationExpressionService;
    @Resource
    private DistributionCabinetTypeSiteService distributionCabinetTypeSiteService;
    @Autowired
    private RedissonClient redisson;
    @Resource
    private DeviceAttributeTenantConfigService deviceAttributeTenantConfigService;
    @Resource
    private DeviceServiceTenantConfigService deviceServiceTenantConfigService;

    @Resource
    private ApplicationContext context;


    private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(SynchronizationLinkthingService.class);

    /**
     * <p>定时任务 每天凌晨1点 执行。加了分布式锁，两个节点只能有一个节点执行</p>
     * <p>获取linthings的 物模型数据。保存到AEP数据库：设备类型、设备型号、设备服务、设备服务参数</p>
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public boolean synchronizationDeviceData() {
        if (!openReceiving) {
            LOGGER.info("openReceiving 为false");
            return false;
        }
        LOGGER.info("synchronizationDeviceData定时任务开始---------------synchronizationDeviceData------------start--");
        RLock redissonLock = redisson.getLock("synchronizationDeviceData_lock");
        if (redissonLock.tryLock()) {
            try {
                // 同步linkthing的设备类型数据
                synchronizeDeviceType();
//                同步设备型号，属性、设备服务、服务参数等
                synchronizeDeviceUnitAttributeServiceParamMultiVersion(openApiService.getDeviceUnitWithDeviceModel());
            } catch (Exception e) {
                LOGGER.error("synchronizationDeviceData error", e);
                e.printStackTrace();
                return false;
            } finally {
                redissonLock.unlock();
            }
        }
        LOGGER.info("synchronizationDeviceData定时任务完成---------------synchronizationDeviceData------------end--");
        return true;
    }

    /**
     * <p>同步设备类型</p>
     * <p>向linkthings查询设备类型，更新到AEP</p>
     */
    private void synchronizeDeviceType() throws UnsupportedEncodingException {
        List<com.easylinkin.linkappapi.openapi.dto.DeviceType> deviceTypeList = openApiService.getDeviceTypeList();
        for (com.easylinkin.linkappapi.openapi.dto.DeviceType dt : deviceTypeList) {
            DeviceType deviceType = new DeviceType().setName(dt.getName());
            List<DeviceType> list = deviceTypeService.getAll(deviceType);
            if (list.isEmpty()) {
                deviceType.setCreateTime(new Date());
                deviceType.setModifyTime(new Date());
                deviceType.setDeleteState(1);
                deviceTypeService.save(deviceType);
            }
        }
    }

    /**
     * 同步设备型号  属性  设备服务  设备服务参数
     */
    @Transactional(rollbackFor = Exception.class)
    public void synchronizeDeviceUnitAttributeServiceParamMultiVersion(List<com.easylinkin.linkappapi.openapi.dto.DeviceUnitDTO> deviceUnitDTOList) {

        if (ObjectUtils.isEmpty(deviceUnitDTOList)) {
            return;
        }
//        LOGGER.info("synchronizeDeviceUnitAttributeServiceParamMultiVersion be called 接口被调用,参数是:{}", JSON.toJSONString(deviceUnitDTOList));
        //  新增的设备属性的id集合
//        Set<String> addDeviceAttrIds = new HashSet<>();
        //  新增的设备服务的id 集合
//        Set<String> addDeviceServicesIds = new HashSet<>();
        // 同步linkthing的设备型号数据
        for (com.easylinkin.linkappapi.openapi.dto.DeviceUnitDTO deviceUnitDTO : deviceUnitDTOList) {
            if (ObjectUtils.isEmpty(deviceUnitDTO.getDeviceVersions())) {
                continue;
            }
            for (DeviceVersion deviceVersion : deviceUnitDTO.getDeviceVersions()) {
                DeviceUnit deviceUnit = new DeviceUnit().setCode(deviceUnitDTO.getDeviceUnitCode()).setVersion(deviceVersion.getDeviceVersion());
                List<DeviceUnit> deviceUnitList = deviceUnitService.getAll(deviceUnit);
                // 是一个新版本的 设备型号                
                boolean isNewVersionDeviceUnit = deviceUnitList.isEmpty();
                if (!isNewVersionDeviceUnit) {
                    deviceUnit = deviceUnitList.get(0);
                }

                com.easylinkin.linkappapi.openapi.dto.DeviceType deviceTypeDto = deviceUnitDTO.getDeviceType();
                if (deviceTypeDto != null && deviceUnitDTO.getDeviceType() != null) {
                    deviceUnit.setDeviceTypeName(deviceUnitDTO.getDeviceType().getName());
                    if (ObjectUtils.isNotEmpty(deviceUnitDTO.getDeviceType().getName())) {
                        List<DeviceType> deviceTypeList = deviceTypeService.getAll(new DeviceType().setName(deviceUnitDTO.getDeviceType().getName()));
                        if (!deviceTypeList.isEmpty()) {
                            deviceUnit.setDeviceTypeId(deviceTypeList.get(0).getId());
                        }
                    }
                }

                deviceUnit.setName(deviceUnitDTO.getDeviceUnitName());
                deviceUnit.setPhysicsModel(deviceVersion.getPhysicsModel());
                deviceUnit.setCreateTime(deviceVersion.getCreateTime());
                deviceUnit.setModifyTime(new Date());
                deviceUnit.setDeleteState(1);

                deviceUnitService.saveOrUpdate(deviceUnit);

                // 更新物模型
                if (deviceVersion.getPhysicsModel() == null) {
                    LOGGER.info("physicsModelJson 为空，跳过该条数据同步，型号code是:{}", deviceUnit.getCode());
                    continue;
                }
                PhysicsModel physicsModel = JSONObject.parseObject(deviceVersion.getPhysicsModel(), PhysicsModel.class);
                if (physicsModel == null) {
                    LOGGER.info("physicsModelJson 解析出physicsModel为空，型号code是:{}", deviceUnit.getCode());
                    continue;
                }

                List<DeviceUnit> oldDeviceUnits = new ArrayList<>();
                if (isNewVersionDeviceUnit) {
                    QueryWrapper<DeviceUnit> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("code", deviceUnit.getCode());
//                    不上上一条新增的
                    queryWrapper.ne("id", deviceUnit.getId());
                    queryWrapper.orderByDesc("create_time");
                    oldDeviceUnits = deviceUnitService.list(queryWrapper);
                }

                //同步设备属性
                synchronizeDeviceAttribute(deviceUnit, isNewVersionDeviceUnit, oldDeviceUnits, physicsModel);
//                Map<String, Object> resultMap = 
//                if (resultMap != null) {
//                    addDeviceAttrIds.addAll((Set<String>) resultMap.get("addDeviceAttrIds"));
//                }
                //同步设备服务
                synchronizeDeviceServices(deviceUnit, oldDeviceUnits, physicsModel);
//                Map<String, Object> resultMap2 = synchronizeDeviceServices(deviceUnit, oldDeviceUnits, physicsModel);
//                if (resultMap2 != null) {
//                    addDeviceServicesIds.addAll((Set<String>) resultMap2.get("addDeviceServicesIds"));
//                }
            }
        }
//        //        对新增的属性，查询旧版的属性是否有个性化配置 如果有修改，用新增的属性id 替换
//        deviceAttributeTenantConfigService.inheritPersonalizedConfig(addDeviceAttrIds);
//        //        对新增的设备服务，查询旧版的设备服务是否有个性化配置 如果有修改，用新增的设备服务id 替换
//
//        deviceServiceTenantConfigService.inheritPersonalizedConfig(addDeviceServicesIds);
    }


    /**
     * 同步设备型号  属性  设备服务  设备服务参数
     */
    @Transactional
    public Map<String, List> synchronizeDeviceUnitAttributeServiceParam(List<com.easylinkin.linkappapi.openapi.dto.DeviceUnit> deviceUnitList, String tenantId) {

        if (ObjectUtils.isEmpty(deviceUnitList)) {
            return null;
        }
//        LOGGER.info("synchronizeDeviceUnitAttributeServiceParam be called 接口被调用,参数是:{}", JSON.toJSONString(deviceUnitList));
        if(StringUtils.isEmpty(tenantId)){
            tenantId = linkappUserContextProducer.getCurrent().getTenantId();
        }

        List<com.easylinkin.linkappapi.openapi.dto.DeviceUnit> noFindDeviceUnitList = new ArrayList<>();
        List<DeviceUnit> updateDeviceUnitList = new ArrayList<>();
        // 同步linkthing的设备型号数据
        for (com.easylinkin.linkappapi.openapi.dto.DeviceUnit deviceDtoUnit : deviceUnitList) {

            if (StringUtils.isEmpty(deviceDtoUnit.getDeviceVersion())) {
                continue;
            }

            String version = deviceDtoUnit.getDeviceVersion();
            DeviceUnit deviceUnit = new DeviceUnit().setCode(deviceDtoUnit.getCode()).setVersion(version);
            List<DeviceUnit> list = deviceUnitService.getAll(deviceUnit);
            //找到这个租户下这个型号的版本号
            TenantRefDeviceUnit refDeviceUnit = tenantRefDeviceUnitMapper.findByTenantIdAndDeviceUnitCode(tenantId,deviceUnit.getCode());
//            如果查找不到型号
            if (ObjectUtils.isNotEmpty(list)) {
                deviceUnit = list.get(0);
                deviceUnit.setDeleteState(1);
                deviceUnit.setName(deviceDtoUnit.getName());
                deviceUnit.setPhysicsModel(deviceDtoUnit.getPhysicsModel());
                if (ObjectUtils.isNotEmpty(deviceDtoUnit.getDeviceType())) {
                    deviceUnit.setDeviceTypeName(deviceDtoUnit.getDeviceType().getName());
                }
                deviceUnitService.updateById(deviceUnit);
                updateDeviceUnitList.add(deviceUnit);
                //比对版本号
                //如果对应的id对不上，则进行更新属性，更新原则是删除再处理
                if(refDeviceUnit!=null){
                    String existRefUnitId = refDeviceUnit.getDeviceUnitId();
                    if(!existRefUnitId.equals(deviceUnit.getId())){
                        //删除已经绑定的个性化参数
                        deviceAttributeTenantConfigService.inheritPersonalizedConfig(existRefUnitId,tenantId,deviceUnit.getId());
                        deviceServiceTenantConfigService.inheritPersonalizedConfig(existRefUnitId,tenantId,deviceUnit.getId());
                    }
                }
//                continue;
            } else {
                noFindDeviceUnitList.add(deviceDtoUnit);

                //如果找不到这个版本的信息，则个性化中直接删除
                if(refDeviceUnit!=null){
                    String existRefUnitId = refDeviceUnit.getDeviceUnitId();
                    deviceAttributeTenantConfigService.deleteByTenantIdAndUnitId(tenantId,existRefUnitId);
                    deviceServiceTenantConfigService.deleteByTenantIdAndUnitId(tenantId,existRefUnitId);
                }
            }
        }

        //  对新增的属性，查询旧版的属性是否有个性化配置 如果有就插入一份个性化配置,属性id取新插入的属性的id
//        LOGGER.info("计算addDeviceAttrIds:{}", JSON.toJSONString(addDeviceAttrIds));


        Map<String, List> map = new HashMap<String, List>();
        map.put("updateDeviceUnitList", updateDeviceUnitList);
        map.put("noFindDeviceUnitList", noFindDeviceUnitList);
        deviceUnitList.removeAll(noFindDeviceUnitList);
        return map;
    }

    private void updateDeviceTypeAttr(com.easylinkin.linkappapi.openapi.dto.DeviceType deviceType, DeviceUnit deviceUnit) {
        if(StringUtils.isNotBlank(deviceType.getName())){
            deviceUnit.setDeviceTypeName(deviceType.getName());
            QueryWrapper<DeviceType> wrapper = new QueryWrapper<>();
            wrapper.eq("name",deviceType.getName());
            DeviceType existOne = deviceTypeService.getOne(wrapper);
            if(existOne!=null){
                deviceUnit.setDeviceTypeId(existOne.getId());
            }
        }
    }

    /**
     * 同步设备服务
     */
    private Map<String, Object> synchronizeDeviceServices(DeviceUnit deviceUnit, List<DeviceUnit> oldDeviceUnits, PhysicsModel physicsModel) {
        // 设备服务
        List<ServicesModel> servicesList = physicsModel.getServices();
        if (ObjectUtils.isEmpty(servicesList)) {
            return null;
        }
        Map<String, Object> resultMap = new HashMap<>();
        Set<String> addDeviceServicesIds = new HashSet();
        int deviceServiceSortNo = 0;
        for (ServicesModel service : servicesList) {
            DeviceServices deviceServices = new DeviceServices();
            deviceServices.setIdentifier(service.getIdentifier());
            deviceServices.setDeviceUnitId(deviceUnit.getId());

            List<DeviceServices> deviceServicesList = deviceServiceService.getDeviceServicesList(deviceServices);
            if (ObjectUtils.isNotEmpty(deviceServicesList)) {
                deviceServices = deviceServicesList.get(0);
            }
            deviceServices.setName(service.getName());
            deviceServices.setAckType(service.getAckType());
            deviceServices.setAckTimeOut(service.getAckTimeout());
            deviceServices.setType(1);
            deviceServices.setVersion(deviceUnit.getVersion());
            deviceServices.setModifyTime(new Date());

            // 如果是修改            
            if (ObjectUtils.isNotEmpty(deviceServicesList)) {
                deviceServiceService.updateById(deviceServices);
            } else {
                //  如果是新增
                deviceServices.setSortNo(deviceServiceSortNo);
                deviceServiceSortNo++;

                if (ObjectUtils.isNotEmpty(oldDeviceUnits)) {
                    DeviceUnit oldDeviceUnit = oldDeviceUnits.get(0);
                    QueryWrapper<DeviceServices> qw = new QueryWrapper();
                    qw.eq("name", service.getName());
                    qw.eq("device_unit_id", oldDeviceUnit.getId());
                    qw.eq("version", oldDeviceUnit.getVersion());
                    qw.orderByDesc("create_time");
                    List<DeviceServices> oldServiceList = deviceServiceService.list(qw);
                    // 继承上一个版本的 排序 和 是否显示
                    if (ObjectUtils.isNotEmpty(oldServiceList)) {
                        deviceServices.setSortNo(oldServiceList.get(0).getSortNo());
                        deviceServices.setIsShow(oldServiceList.get(0).getIsShow());
                    }
                }
                deviceServices.setCreateTime(new Date());
                deviceServiceService.save(deviceServices);
//                addDeviceServicesIds.add(deviceServices.getId());
            }
            addDeviceServicesIds.add(deviceServices.getId());
            //  同步设备服务参数            
            synchronizeDeviceParam(deviceUnit, service, deviceServices);
        }
        //        新增的设备属性的id集合
        resultMap.put("addDeviceServicesIds", addDeviceServicesIds);
        return resultMap;
    }

    /**
     * 同步设备服务参数
     *
     * @param deviceUnit     设备型号
     * @param service        服务模型
     * @param deviceServices 设备服务
     */
    private void synchronizeDeviceParam(DeviceUnit deviceUnit, ServicesModel service, DeviceServices deviceServices) {
        // 参数
        if (ObjectUtils.isEmpty(service.getInputs())) {
            return;
        }
        int deviceParmSortNo = 0;
        for (com.easylinkin.linkappapi.openapi.dto.Input input : service.getInputs()) {
            deviceParmSortNo++;
            DeviceParm deviceParm = new DeviceParm();
            deviceParm.setIdentifier(input.getIdentifier());
            deviceParm.setDeviceServiceId(deviceServices.getId());
            List<DeviceParm> deviceParmList = deviceParmMapper.selectDeviceParmList(deviceParm);
            if (deviceParmList.isEmpty()) {
                deviceParm.setName(input.getName());
                if ("struct".equals(input.getDataType().getType())) {
                    deviceParm.setUnit(PhysicalModelTypeConstant.OBJECT);
                } else {
                    if ("array".equals(input.getDataType().getType())) {
                        DataType dataType = JSONObject.parseObject(input.getDataType().getSpecs(), DataType.class);
                        if (dataType != null && "struct".equals(dataType.getType())) {
                            deviceParm.setUnit(PhysicalModelTypeConstant.ARRAY_OBJECT);
                        }
                    } else {
                        deviceParm.setUnit(input.getDataType().getType());
                    }
                }
                deviceParm.setSpecs(input.getDataType().getSpecs());
                deviceParm.setSortNo(deviceParmSortNo);
                deviceParm.setCreateTime(new Date());
                deviceParm.setVersion(deviceUnit.getVersion());
                deviceParmService.save(deviceParm);
                saveOrUpdateNodeParm(deviceUnit.getVersion(), deviceServices, input, deviceParm);

            } else {
                deviceParm = deviceParmList.get(0);
                deviceParm.setName(input.getName());
                if ("struct".equals(input.getDataType().getType())) {
                    deviceParm.setUnit(PhysicalModelTypeConstant.OBJECT);
                } else {
                    if ("array".equals(input.getDataType().getType())) {
                        DataType dataType = JSONObject.parseObject(input.getDataType().getSpecs(), DataType.class);
                        if (dataType != null && "struct".equals(dataType.getType())) {
                            deviceParm.setUnit(PhysicalModelTypeConstant.ARRAY_OBJECT);
                        }
                    } else {
                        deviceParm.setUnit(input.getDataType().getType());
                    }
                }
                deviceParm.setSpecs(input.getDataType().getSpecs());
                deviceParm.setSortNo(deviceParmSortNo);
                deviceParm.setVersion(deviceUnit.getVersion());
                deviceParm.setModifyTime(new Date());
                deviceParmService.updateById(deviceParm);
                saveOrUpdateNodeParm(deviceUnit.getVersion(), deviceServices, input, deviceParm);
            }

        }
    }

    /**
     * 同步设备属性
     */
    private Map<String, Object> synchronizeDeviceAttribute(DeviceUnit deviceUnit, boolean isNewVersionDeviceUnit, List<DeviceUnit> oldDeviceUnits, PhysicsModel physicsModel) {

        List<PropertiesModel> physicsModelProperties = physicsModel.getProperties();
        if (ObjectUtils.isEmpty(physicsModelProperties)) {
            return null;
        }
        Map<String, Object> resultMap = new HashMap<>();
        // 属性序号
        int attrSortNo = 1;
        // 新增的设备属性id集
        Set<String> addDeviceAttrIds = new HashSet<>();
        for (PropertiesModel propertiesModel : physicsModelProperties) {
            DeviceAttribute deviceAttr = new DeviceAttribute().setIdentifier(propertiesModel.getIdentifier()).setDeviceUnitId(deviceUnit.getId());
            List<DeviceAttribute> deviceAttrList = deviceAttributeService.getAll2(deviceAttr);
            if (ObjectUtils.isNotEmpty(deviceAttrList)) {
                deviceAttr = deviceAttrList.get(0);
                deviceAttr.setModifyTime(new Date());
            }
            deviceAttr.setName(propertiesModel.getName());
            deviceAttr.setSpecs(propertiesModel.getDataType().getSpecs());
            deviceAttr.setVersion(deviceUnit.getVersion());
            setDeviceAttrComplexUnit(deviceAttr, propertiesModel);

            if (ObjectUtils.isNotEmpty(deviceAttrList)) {
                deviceAttributeService.updateById(deviceAttr);
            } else {
                deviceAttr.setSortNo(attrSortNo);
                attrSortNo++;
                if (isNewVersionDeviceUnit && ObjectUtils.isNotEmpty(oldDeviceUnits)) {
                    DeviceUnit oldDeviceUnit = oldDeviceUnits.get(0);
                    DeviceAttribute oldDeviceAttrQuery = new DeviceAttribute().setIdentifier(propertiesModel.getIdentifier()).setDeviceUnitId(oldDeviceUnit.getId());
                    List<DeviceAttribute> oldDeviceAttrList = deviceAttributeService.getAll2(oldDeviceAttrQuery);
                    if (ObjectUtils.isNotEmpty(oldDeviceAttrList)) {
                        deviceAttr.setSortNo(oldDeviceAttrList.get(0).getSortNo());
                        deviceAttr.setIsShow(oldDeviceAttrList.get(0).getIsShow());
                        deviceAttr.setIcoPath(oldDeviceAttrList.get(0).getIcoPath());
                    }
                }
                deviceAttr.setCreateTime(new Date());
                deviceAttributeService.save(deviceAttr);
//                addDeviceAttrIds.add(deviceAttr.getId());
            }
            addDeviceAttrIds.add(deviceAttr.getId());
            saveOrUpdateNodeAttr(deviceAttr, deviceUnit, propertiesModel);
        }
        //        新增的设备属性的id集合
        resultMap.put("addDeviceAttrIds", addDeviceAttrIds);
        return resultMap;
    }

    /**
     * 为设备属性设置复杂Unit  -例如 object、array-object array-simple
     */
    private void setDeviceAttrComplexUnit(DeviceAttribute deviceAttr, PropertiesModel propertiesModel) {
        if ("struct".equals(propertiesModel.getDataType().getType())) {
            deviceAttr.setUnit(PhysicalModelTypeConstant.OBJECT);
        } else {
            if ("array".equals(propertiesModel.getDataType().getType())) {
                DataType dataType = JSONObject.parseObject(propertiesModel.getDataType().getSpecs(), DataType.class);
                if (dataType != null && "struct".equals(dataType.getType())) {
                    deviceAttr.setUnit(PhysicalModelTypeConstant.ARRAY_OBJECT);
                } else {
                    deviceAttr.setUnit(PhysicalModelTypeConstant.ARRAY_SIMPLE);
                }
            } else {
                deviceAttr.setUnit(propertiesModel.getDataType().getType());
            }
        }
    }

    private void saveOrUpdateNodeParm(String version, DeviceServices deviceServices,
                                      com.easylinkin.linkappapi.openapi.dto.Input input, DeviceParm deviceParm) {
        if ("struct".equalsIgnoreCase(input.getDataType().getType())) {
            List<PropertiesModel> propertiesModelNodes = JSONObject.parseArray(input.getDataType().getSpecs(), PropertiesModel.class);

            if (!propertiesModelNodes.isEmpty()) {
                int nodeSort = 0;
                for (PropertiesModel nodePorp : propertiesModelNodes) {
                    DeviceParm nodeDeviceParm = new DeviceParm();
                    nodeDeviceParm.setIdentifier(nodePorp.getIdentifier());
                    nodeDeviceParm.setDeviceServiceId(deviceServices.getId());
//					nodeDeviceParm.setVersion(version);
                    List<DeviceParm> deviceParmNodeList = deviceParmMapper.selectDeviceParmList(nodeDeviceParm);
                    if (deviceParmNodeList.isEmpty()) {
                        nodeDeviceParm.setName(nodePorp.getName());
                        if ("struct".equals(nodePorp.getDataType().getType())) {
                            nodeDeviceParm.setUnit(PhysicalModelTypeConstant.OBJECT);
                        } else {
                            if ("array".equals(nodePorp.getDataType().getType())) {
                                DataType dataType = JSONObject.parseObject(input.getDataType().getSpecs(), DataType.class);
                                if (dataType != null && "struct".equals(dataType.getType())) {
                                    nodeDeviceParm.setUnit(PhysicalModelTypeConstant.ARRAY_OBJECT);
                                }
                            } else {
                                nodeDeviceParm.setUnit(nodePorp.getDataType().getType());
                            }
                        }
                        nodeDeviceParm.setSpecs(nodePorp.getDataType().getSpecs());
                        nodeDeviceParm.setSortNo(nodeSort);
                        nodeDeviceParm.setCreateTime(new Date());
                        nodeDeviceParm.setParentId(deviceParm.getId());
                        nodeDeviceParm.setVersion(version);
                        deviceParmService.save(nodeDeviceParm);
                    } else {
                        nodeDeviceParm = deviceParmNodeList.get(0);
                        nodeDeviceParm.setName(nodePorp.getName());
                        if ("struct".equals(nodePorp.getDataType().getType())) {
                            nodeDeviceParm.setUnit(PhysicalModelTypeConstant.OBJECT);
                        } else {
                            if ("array".equals(nodePorp.getDataType().getType())) {
                                DataType dataType = JSONObject.parseObject(nodePorp.getDataType().getSpecs(), DataType.class);
                                if (dataType != null && "struct".equals(dataType.getType())) {
                                    nodeDeviceParm.setUnit(PhysicalModelTypeConstant.ARRAY_OBJECT);
                                }
                            } else {
                                nodeDeviceParm.setUnit(nodePorp.getDataType().getType());
                            }
                        }
                        nodeDeviceParm.setSpecs(nodePorp.getDataType().getSpecs());
                        nodeDeviceParm.setSortNo(nodeSort);
                        nodeDeviceParm.setModifyTime(new Date());
                        nodeDeviceParm.setParentId(deviceParm.getId());
                        nodeDeviceParm.setVersion(version);
                        deviceParmService.updateById(nodeDeviceParm);
                    }
                    nodeSort++;
                }
            }
        }

        if ("array".equalsIgnoreCase(input.getDataType().getType())) {
            DataType dataType = JSONObject.parseObject(input.getDataType().getSpecs(), DataType.class);
            if (dataType != null && "struct".equals(dataType.getType())) {
                List<PropertiesModel> propertiesModelNodes = JSONObject.parseArray(dataType.getSpecs(), PropertiesModel.class);
                if (!propertiesModelNodes.isEmpty()) {
                    int nodeSort = 0;
                    for (PropertiesModel nodePorp : propertiesModelNodes) {
                        DeviceParm nodeDeviceParm = new DeviceParm();
                        nodeDeviceParm.setIdentifier(nodePorp.getIdentifier());
                        nodeDeviceParm.setDeviceServiceId(deviceServices.getId());
                        List<DeviceParm> deviceParmNodeList = deviceParmMapper.selectDeviceParmList(nodeDeviceParm);
                        if (deviceParmNodeList.isEmpty()) {
                            nodeDeviceParm.setName(nodePorp.getName());
                            if ("struct".equals(nodePorp.getDataType().getType())) {
                                nodeDeviceParm.setUnit(PhysicalModelTypeConstant.OBJECT);
                            } else {
                                if ("array".equals(nodePorp.getDataType().getType())) {
                                    dataType = JSONObject.parseObject(nodePorp.getDataType().getSpecs(), DataType.class);
                                    if (dataType != null && "struct".equals(dataType.getType())) {
                                        nodeDeviceParm.setUnit(PhysicalModelTypeConstant.ARRAY_OBJECT);
                                    }
                                } else {
                                    nodeDeviceParm.setUnit(nodePorp.getDataType().getType());
                                }
                            }
                            nodeDeviceParm.setSpecs(nodePorp.getDataType().getSpecs());
                            nodeDeviceParm.setSortNo(nodeSort);
                            nodeDeviceParm.setCreateTime(new Date());
                            nodeDeviceParm.setParentId(deviceParm.getId());
                            nodeDeviceParm.setVersion(version);
                            deviceParmService.save(nodeDeviceParm);
                        } else {
                            nodeDeviceParm = deviceParmNodeList.get(0);
                            nodeDeviceParm.setName(nodePorp.getName());
                            if ("struct".equals(nodePorp.getDataType().getType())) {
                                nodeDeviceParm.setUnit(PhysicalModelTypeConstant.OBJECT);
                            } else {
                                if ("array".equals(nodePorp.getDataType().getType())) {
                                    dataType = JSONObject.parseObject(nodePorp.getDataType().getSpecs(), DataType.class);
                                    if (dataType != null && "struct".equals(dataType.getType())) {
                                        nodeDeviceParm.setUnit(PhysicalModelTypeConstant.ARRAY_OBJECT);
                                    }
                                } else {
                                    nodeDeviceParm.setUnit(nodePorp.getDataType().getType());
                                }
                            }
                            nodeDeviceParm.setSpecs(nodePorp.getDataType().getSpecs());
                            nodeDeviceParm.setSortNo(nodeSort);
                            nodeDeviceParm.setModifyTime(new Date());
                            nodeDeviceParm.setParentId(deviceParm.getId());
                            nodeDeviceParm.setVersion(version);
                            deviceParmService.updateById(nodeDeviceParm);
                        }
                        nodeSort++;
                    }
                }
            } else {
                dataType = JSONObject.parseObject(input.getDataType().getSpecs(), DataType.class);
                DeviceParm nodeDeviceParm = new DeviceParm();
                nodeDeviceParm.setIdentifier(input.getIdentifier());
                nodeDeviceParm.setDeviceServiceId(deviceServices.getId());
                List<DeviceParm> deviceParmNodeList = deviceParmMapper.selectDeviceParmList(nodeDeviceParm);
                if (deviceParmNodeList.isEmpty()) {
                    nodeDeviceParm.setName(input.getName());
                    if ("array".equals(input.getDataType().getType())) {
                        nodeDeviceParm.setUnit(PhysicalModelTypeConstant.ARRAY_SIMPLE);
                    } else {
                        nodeDeviceParm.setUnit(input.getDataType().getType());
                    }

                    nodeDeviceParm.setSpecs(dataType.getSpecs());
                    nodeDeviceParm.setSortNo(0);
                    nodeDeviceParm.setCreateTime(new Date());
                    nodeDeviceParm.setVersion(version);
                    deviceParmService.save(nodeDeviceParm);
                } else {
                    nodeDeviceParm = deviceParmNodeList.get(0);
                    nodeDeviceParm.setName(input.getName());
                    if ("array".equals(input.getDataType().getType())) {
                        nodeDeviceParm.setUnit(PhysicalModelTypeConstant.ARRAY_SIMPLE);
                    } else {
                        nodeDeviceParm.setUnit(input.getDataType().getType());
                    }
                    nodeDeviceParm.setSpecs(input.getDataType().getSpecs());
                    nodeDeviceParm.setSortNo(0);
                    nodeDeviceParm.setModifyTime(new Date());
                    nodeDeviceParm.setVersion(version);
                    deviceParmService.updateById(nodeDeviceParm);
                }
            }
        }
    }

    private void saveOrUpdateNodeAttr(DeviceAttribute deviceAttr, DeviceUnit deviceUnit, PropertiesModel propertiesModel) {
        // emp物模型struct类型 转换成object
        if ("struct".equalsIgnoreCase(propertiesModel.getDataType().getType())) {
            List<PropertiesModel> propertiesModelNodes = JSONObject.parseArray(propertiesModel.getDataType().getSpecs(), PropertiesModel.class);
            if (!propertiesModelNodes.isEmpty()) {
                int nodeSort = 0;
                for (PropertiesModel nodePorp : propertiesModelNodes) {
                    DeviceAttribute nodeDeviceAttr = new DeviceAttribute();
                    nodeDeviceAttr.setIdentifier(nodePorp.getIdentifier());
                    nodeDeviceAttr.setParentId(deviceAttr.getId());
                    nodeDeviceAttr.setDeviceUnitId(deviceUnit.getId());
                    List<DeviceAttribute> deviceAttrNodeList = deviceAttributeService.getAll2(nodeDeviceAttr);

                    if (deviceAttrNodeList.isEmpty()) {
                        nodeDeviceAttr.setName(nodePorp.getName());
                        nodeDeviceAttr.setUnit(nodePorp.getDataType().getType());
                        nodeDeviceAttr.setSpecs(nodePorp.getDataType().getSpecs());
                        nodeDeviceAttr.setSortNo(nodeSort);
                        nodeDeviceAttr.setCreateTime(new Date());
                        nodeDeviceAttr.setParentId(deviceAttr.getId());
                        nodeDeviceAttr.setIsShow(deviceAttr.getIsShow());
                        nodeDeviceAttr.setVersion(deviceUnit.getVersion());
                        deviceAttributeService.save(nodeDeviceAttr);
                    } else {
                        nodeDeviceAttr = deviceAttrNodeList.get(0);
                        nodeDeviceAttr.setName(nodePorp.getName());
                        nodeDeviceAttr.setUnit(nodePorp.getDataType().getType());
                        nodeDeviceAttr.setSpecs(nodePorp.getDataType().getSpecs());
//						nodeDeviceAttr.setSortNo(nodeSort);
                        nodeDeviceAttr.setModifyTime(new Date());
                        nodeDeviceAttr.setParentId(deviceAttr.getId());
                        nodeDeviceAttr.setVersion(deviceUnit.getVersion());
                        deviceAttributeService.updateById(nodeDeviceAttr);
                    }
                    nodeSort++;
                }
            }
        }
        // 区分array及array-object
        if ("array".equalsIgnoreCase(propertiesModel.getDataType().getType())) {
            DataType dataType = JSONObject.parseObject(propertiesModel.getDataType().getSpecs(), DataType.class);
            if (dataType != null && "struct".equals(dataType.getType())) {
                List<PropertiesModel> propertiesModelNodes = JSONObject.parseArray(dataType.getSpecs(), PropertiesModel.class);
                if (!propertiesModelNodes.isEmpty()) {
                    int nodeSort = 0;
                    for (PropertiesModel nodePorp : propertiesModelNodes) {
                        DeviceAttribute nodeDeviceAttr = new DeviceAttribute();
                        nodeDeviceAttr.setIdentifier(nodePorp.getIdentifier());
                        nodeDeviceAttr.setDeviceUnitId(deviceUnit.getId());
                        List<DeviceAttribute> deviceAttrNodeList = deviceAttributeService.getAll2(nodeDeviceAttr);
                        if (deviceAttrNodeList.isEmpty()) {
                            nodeDeviceAttr.setName(nodePorp.getName());
                            nodeDeviceAttr.setUnit(nodePorp.getDataType().getType());
                            nodeDeviceAttr.setSpecs(nodePorp.getDataType().getSpecs());
                            nodeDeviceAttr.setSortNo(nodeSort);
                            nodeDeviceAttr.setCreateTime(new Date());
                            nodeDeviceAttr.setParentId(deviceAttr.getId());
                            nodeDeviceAttr.setVersion(deviceUnit.getVersion());
                            deviceAttributeService.save(nodeDeviceAttr);
                        } else {
                            nodeDeviceAttr = deviceAttrNodeList.get(0);
                            nodeDeviceAttr.setName(nodePorp.getName());
                            nodeDeviceAttr.setUnit(nodePorp.getDataType().getType());
                            nodeDeviceAttr.setSpecs(nodePorp.getDataType().getSpecs());
//							nodeDeviceAttr.setSortNo(nodeSort);
                            nodeDeviceAttr.setModifyTime(new Date());
                            nodeDeviceAttr.setParentId(deviceAttr.getId());
                            nodeDeviceAttr.setVersion(deviceUnit.getVersion());
                            deviceAttributeService.updateById(nodeDeviceAttr);
                        }
                        nodeSort++;
                    }
                }
            } else {
                dataType = JSONObject.parseObject(propertiesModel.getDataType().getSpecs(), DataType.class);
                DeviceAttribute nodeDeviceAttr = new DeviceAttribute();
                nodeDeviceAttr.setIdentifier(propertiesModel.getIdentifier());
                nodeDeviceAttr.setDeviceUnitId(deviceUnit.getId());
                List<DeviceAttribute> deviceAttrNodeList = deviceAttributeService.getAll(nodeDeviceAttr);
                if (deviceAttrNodeList.isEmpty()) {
                    nodeDeviceAttr.setName(propertiesModel.getName());
                    if ("array".equals(propertiesModel.getDataType().getType())) {
                        nodeDeviceAttr.setUnit(PhysicalModelTypeConstant.ARRAY_SIMPLE);
                    } else {
                        nodeDeviceAttr.setUnit(propertiesModel.getDataType().getType());
                    }
                    nodeDeviceAttr.setUnit(propertiesModel.getDataType().getType());
                    nodeDeviceAttr.setSpecs(dataType.getSpecs());
                    nodeDeviceAttr.setSortNo(0);
                    nodeDeviceAttr.setCreateTime(new Date());
                    nodeDeviceAttr.setVersion(deviceUnit.getVersion());
                    deviceAttributeService.save(nodeDeviceAttr);
                } else {
                    nodeDeviceAttr = deviceAttrNodeList.get(0);
                    nodeDeviceAttr.setName(propertiesModel.getName());
                    if ("array".equals(propertiesModel.getDataType().getType())) {
                        nodeDeviceAttr.setUnit(PhysicalModelTypeConstant.ARRAY_SIMPLE);
                    } else {
                        nodeDeviceAttr.setUnit(propertiesModel.getDataType().getType());
                    }
                    nodeDeviceAttr.setSpecs(propertiesModel.getDataType().getSpecs());
                    nodeDeviceAttr.setSortNo(0);
                    nodeDeviceAttr.setModifyTime(new Date());
                    nodeDeviceAttr.setVersion(deviceUnit.getVersion());
                    deviceAttributeService.updateById(nodeDeviceAttr);
                }
            }
        }
    }


    /**
     * 前端，emp后端全局查询没地方用到该接口
     * @throws UnsupportedEncodingException
     */
    @Deprecated
    public void synchronizeDeviceUnitAndDevicesByProjectByTime() throws UnsupportedEncodingException {

        List<LinkappTenant> tenants = tenantService.selectLinkappTenantList(new LinkappTenant());

        for (LinkappTenant tenant : tenants) {
            if (tenant.getProjectId() != null) {
                this.synchronizeDeviceUnitByProject(tenant);
                this.getDevicesByProject(tenant);
            }
        }
    }

    /**
     * 前端，emp后端全局查询没地方用到该接口
     * @param tenant
     * @return
     * @throws UnsupportedEncodingException
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public List<com.easylinkin.linkappapi.openapi.dto.DeviceUnit> synchronizeDeviceUnitByProject(LinkappTenant tenant) throws UnsupportedEncodingException {
//		从linkthings 查询设备型号
        List<com.easylinkin.linkappapi.openapi.dto.DeviceUnit> deviceUnits = openApiService.getDeviceUnitByProject(tenant);
//		同步设备型号  属性  设备服务  设备服务参数
        synchronizeDeviceUnitAttributeServiceParam(deviceUnits, tenant.getId());

        List<DeviceUnit> dus = new ArrayList<>();
        if (deviceUnits != null) {
            for (com.easylinkin.linkappapi.openapi.dto.DeviceUnit du : deviceUnits) {
                if (du.getDeviceVersion() == null) {
                    continue;
                }
                QueryWrapper<DeviceUnit> qw = new QueryWrapper<>();
                qw.eq("code", du.getCode());
                qw.eq("version", du.getDeviceVersion());
//                qw.isNotNull("linkapp_flag");
                DeviceUnit deviceUnit = deviceUnitMapper.selectOne(qw);
                if(ObjectUtils.isNotEmpty(deviceUnit)){
                    dus.add(deviceUnit);
                }
            }
        }
//        LOGGER.info("json format dus=" + JSON.toJSONString(dus));
        if(ObjectUtils.isNotEmpty(dus)){
            QueryWrapper<TenantRefDeviceUnit> deleteQw = new QueryWrapper<>();
            deleteQw.eq("tenant_id", tenant.getId());
            tenantRefDeviceUnitMapper.delete(deleteQw);
            for (DeviceUnit duQuery : dus) {
                if(duQuery == null){
                    continue;
                }
                TenantRefDeviceUnit tenantRefDeviceUnit = new TenantRefDeviceUnit();
                tenantRefDeviceUnit.setDeviceUnitId(duQuery.getId());
                tenantRefDeviceUnit.setTenantId(tenant.getId());
                tenantRefDeviceUnitMapper.insert(tenantRefDeviceUnit);
            }
        }
        return deviceUnits;
    }

    /**
     * 同设备管理--同步设备
     * @return
     */
    @Async
    public Future<String> syncDeviceAndUnitByProject(HttpServletRequest request, LinkappTenant tenant) {
        LOGGER.info("异步设备同步开始syncDeviceAndUnitByProject" + JSON.toJSONString(tenant));
        StringBuilder sb = new StringBuilder();
        try {
            boolean b = innerLogin(request, tenant.getId());
            if(!b){
                LOGGER.info("内部登录失败");
                return null;
            }

            // 同步设备型号
            Map<String, List> deviceUnitmap = this.synchronizeDeviceUnitByProject();
            // 同步设备
            Map<String, List> devicemap = this.getDevicesByProject();

            if (deviceUnitmap != null) {
                sb.append("AEP 未找到的设备型号个数:");
                sb.append(deviceUnitmap.get("noFindDeviceUnitList").size());
                sb.append("<br>");
                sb.append("AEP更新设备型号个数:");
                sb.append(deviceUnitmap.get("updateDeviceUnitList").size());
                sb.append("<br>");
            }
            if (devicemap != null) {
                sb.append("新建设备个数:");
                sb.append(devicemap.get("add").size());
                sb.append("<br>");
                sb.append("更新设备个数:");
                sb.append(devicemap.get("update").size());
                sb.append("<br>");
                sb.append("错误设备个数:");
                sb.append(devicemap.get("error").size());
            }
            //设备型号更新触发数据一致性变更
            context.publishEvent(new DeviceUnitVersionUpdateEvent(deviceUnitmap));
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
        LOGGER.info("异步设备同步结束...");
        return new AsyncResult<String>(sb.toString());
    }

    /**
     * 内部登录实现
     * @param request
     * @param tenantId
     * @return
     */
    private boolean innerLogin(HttpServletRequest request, String tenantId){
        QueryWrapper qw = new QueryWrapper();
        qw.eq("type", "1");
        qw.eq("tenant_id", tenantId);
        qw.eq("delete_state", "1");
        List<LinkappUser> list = linkappUserMapper.selectList(qw);
        if(ObjectUtils.isEmpty(list)){
            LOGGER.info("innerLogin tenantId=" + tenantId + "无此租户用户信息");
            return false;
        }
        LinkappUser user = list.get(0);
        boolean enabled = Optional.ofNullable(user.getDisplay()).orElse(true);
        boolean accountNonLocked = !Optional.ofNullable(user.getLocked()).orElse(false);

        UserDetails userDetails =  new org.springframework.security.core.userdetails.User(user.getUsername(),
                user.getPassword(), enabled, true, true, accountNonLocked, Collections.emptyList());

        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null,
                userDetails.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        // 设置为已登录
        SecurityContextHolder.getContext().setAuthentication(authentication);
        return true;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Map<String, List> synchronizeDeviceUnitByProject() throws UnsupportedEncodingException {

//		从linkthings 查询设备型号
        List<com.easylinkin.linkappapi.openapi.dto.DeviceUnit> deviceUnits = openApiService.getDeviceUnitByProject();
//		同步设备型号  属性  设备服务  设备服务参数
        Map<String, List> map = synchronizeDeviceUnitAttributeServiceParam(deviceUnits, null);
        if (map == null) {
            map = new HashMap<>(4);
            map.put("updateDeviceUnitList", new ArrayList());
            map.put("noFindDeviceUnitList", new ArrayList());
            map.put("add", new ArrayList<TenantRefDeviceUnit>());
            map.put("delete", new ArrayList<TenantRefDeviceUnit>());
            return map;
        }
        map.put("add", new ArrayList<TenantRefDeviceUnit>());
        map.put("delete", new ArrayList<TenantRefDeviceUnit>());
        LinkappTenant tenant = tenantService.currentTenant();
        Assert.notNull(tenant, "租户为空");
        List<DeviceUnit> dus = new ArrayList<>();
        for (com.easylinkin.linkappapi.openapi.dto.DeviceUnit du : deviceUnits) {
            if (du.getDeviceVersion() == null) {
                continue;
            }
            QueryWrapper<DeviceUnit> qw = new QueryWrapper<>();
            qw.eq("code", du.getCode());
            qw.eq("version", du.getDeviceVersion());
            DeviceUnit deviceUnit = deviceUnitMapper.selectOne(qw);
            dus.add(deviceUnit);
            qw = new QueryWrapper<>();
            qw.eq("code", du.getCode());
            List<DeviceUnit> deviceUnitList = deviceUnitMapper.selectList(qw);

            for (DeviceUnit deleteDu : deviceUnitList) {
                QueryWrapper<TenantRefDeviceUnit> deleteQw = new QueryWrapper<>();
                deleteQw.eq("tenant_id", tenant.getId());
                deleteQw.eq("device_unit_id", deleteDu.getId());
                tenantRefDeviceUnitMapper.delete(deleteQw);
                TenantRefDeviceUnit tenantRefDeviceUnit = new TenantRefDeviceUnit();
                tenantRefDeviceUnit.setDeviceUnitId(deleteDu.getId());
                tenantRefDeviceUnit.setTenantId(tenant.getId());
                List<TenantRefDeviceUnit> deleteList = map.get("delete");
                deleteList.add(tenantRefDeviceUnit);
            }
        }

        for (DeviceUnit duQuery : dus) {
            TenantRefDeviceUnit tenantRefDeviceUnit = new TenantRefDeviceUnit();
            tenantRefDeviceUnit.setDeviceUnitId(duQuery.getId());
            tenantRefDeviceUnit.setTenantId(tenant.getId());
            tenantRefDeviceUnitMapper.insert(tenantRefDeviceUnit);
            List<TenantRefDeviceUnit> addList = map.get("add");
            addList.add(tenantRefDeviceUnit);

        }

        return map;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void getDevicesByProject(LinkappTenant tenant) throws UnsupportedEncodingException {
        if (tenant == null || ObjectUtils.isEmpty(tenant.getProjectId())) {
            return;
        }
        int page = 0;
        while (true) {
            List<DeviceByProject> devices = openApiService.getDevicesByProject(tenant.getProjectId(), page++);
            if (ObjectUtils.isEmpty(devices)) {
                break;
            }
            Device device;
            for (DeviceByProject deviceByProject : devices) {
                List<Device> deviceList = deviceMapper.selectDeviceByCodeGlobal(deviceByProject.getDevice_id());
                if (deviceList.isEmpty()) {
                    if (deviceByProject.getDeviceUnit() != null && deviceByProject.getDeviceUnit().getDeviceVersion() != null) {
                        com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit deviceUnit = new com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit();
                        deviceUnit.setCode(deviceByProject.getDeviceUnit().getCode());
                        deviceUnit.setVersion(deviceByProject.getDeviceUnit().getDeviceVersion());
                        List<com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit> list = deviceUnitService.getAll(deviceUnit);
                        if (!list.isEmpty()) {
                            device = new Device();
                            device.setDeviceUnitId(list.get(0).getId());
                            device.setCode(deviceByProject.getDevice_id());
                            device.setName(deviceByProject.getName());
                            device.setTenantId(tenant.getId());
                            device.setCreateTime(new Date());
                            if (deviceByProject.getState() == 1) {
                                device.setOnlineState(1);
                            } else {
                                device.setOnlineState(0);
                            }
//                            LOGGER.info("getDevicesByProject save:" + JSON.toJSONString(device));
                            deviceService.save(device);
                        }
                    }
                } else {
                    device = deviceList.get(0);
                    if (deviceByProject.getDeviceUnit() != null && deviceByProject.getDeviceUnit().getDeviceVersion() != null) {
                        com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit deviceUnit = new com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit();
                        deviceUnit.setCode(deviceByProject.getDeviceUnit().getCode());
                        deviceUnit.setVersion(deviceByProject.getDeviceUnit().getDeviceVersion());
                        List<com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit> list = deviceUnitService.getAll(deviceUnit);
                        if (!list.isEmpty()) {
//                            String id = device.getId();
//                            device= new Device();
//                            device.setId(id);
                            device.setDeviceUnitId(list.get(0).getId());
                            device.setCode(deviceByProject.getDevice_id());
                            device.setName(deviceByProject.getName());
                            device.setTenantId(tenant.getId());
                            device.setModifyTime(new Date());
                            if (deviceByProject.getState() == 1) {
                                device.setOnlineState(1);
                            } else {
                                device.setOnlineState(0);
                            }
//                            new LambdaUpdateChainWrapper<>(deviceMapper)
//                                    .eq(Device::getId, device.getId())
//                                    .update(device);
                            deviceService.updateById(device);
                            synchronizaDistributioncabinet(tenant.getId(), list.get(0).getCode(), list.get(0).getVersion());
                        }
                    }
                }
            }
        }
    }

    private void synchronizaDistributioncabinet(String tenantId, String deviceUnitCode, String version) {
        List<DistributionCabinetConfigurationExpression> dcces = distributionCabinetConfigurationExpressionService
                .getDistributionCabinetConfigurationExpressionByTenant(tenantId, deviceUnitCode);
        List<DistributionCabinetTypeSite> dctss = distributionCabinetTypeSiteService
                .getDistributionCabinetTypeSiteByTenant(tenantId, deviceUnitCode);

        for (DistributionCabinetConfigurationExpression dcce : dcces) {
            UpdateWrapper<DistributionCabinetConfigurationExpression> dcceUpdateWrapper = new UpdateWrapper<>();
            dcceUpdateWrapper.eq("id", dcce.getId());
            dcceUpdateWrapper.set("device_unit_version", version);
            distributionCabinetConfigurationExpressionService.update(dcceUpdateWrapper);
        }

        for (DistributionCabinetTypeSite dcts : dctss) {
            UpdateWrapper<DistributionCabinetTypeSite> dctsUpdateWrapper = new UpdateWrapper<>();
            dctsUpdateWrapper.eq("id", dcts.getId());
            dctsUpdateWrapper.set("device_unit_version", version);
            distributionCabinetTypeSiteService.update(dctsUpdateWrapper);
        }
    }

    @Resource
    private LinkappAreaMapper linkappAreaMapper;

    @Resource
    private LinkappSpaceMapper linkappSpaceMapper;


    /**
     * 根绝projectId获取设备列表
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Map<String, List> getDevicesByProject() throws UnsupportedEncodingException {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        LinkappTenant tenant = new LinkappTenant();
        tenant.setId(tenantId);
        tenant = tenantMapper.selectLinkappTenant(tenant);

        List<Device> updateDeviceList = new ArrayList<>();
        List<Device> addDeviceList = new ArrayList<>();
        List<Device> errorDeviceList = new ArrayList<>();
        Map<String, List> map = new HashMap<>();
        map.put("add", addDeviceList);
        map.put("update", updateDeviceList);
        map.put("error", errorDeviceList);
        if (tenant == null || ObjectUtils.isEmpty(tenant.getProjectId())) {
            return map;
        }

        List<Device> deviceAllList = deviceMapper.selectDevices(new Device());
        Map<String, Device> deviceMap = new HashMap<>();
        for (Device deviceByAll : deviceAllList) {
            deviceMap.put(deviceByAll.getCode(), deviceByAll);
        }

        int page = 0;
        while (true) {
//            从linkthings 获取的设备
            List<DeviceByProject> devices = openApiService.getDevicesByProject(tenant.getProjectId(), page++);
            if (ObjectUtils.isEmpty(devices)) {
                break;
            }
            Device device;
            for (DeviceByProject deviceByProject : devices) {
                if (deviceMap.containsKey(deviceByProject.getDevice_id())) {
                    deviceMap.remove(deviceByProject.getDevice_id());
                }
                List<Device> deviceList = deviceMapper.selectDeviceByCodeGlobal(deviceByProject.getDevice_id());
                if (deviceList.isEmpty()) {
                    boolean noFundDeviceSaveSucc = false;
                    if (deviceByProject.getDeviceUnit() != null && deviceByProject.getDeviceUnit().getDeviceVersion() != null) {
                        com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit deviceUnit = new com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit();
                        deviceUnit.setCode(deviceByProject.getDeviceUnit().getCode());
                        deviceUnit.setVersion(deviceByProject.getDeviceUnit().getDeviceVersion());
                        List<com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit> list = deviceUnitService.getAll(deviceUnit);
                        if (!list.isEmpty()) {
                            device = new Device();
                            device.setDeviceUnitId(list.get(0).getId());
                            device.setCode(deviceByProject.getDevice_id());
                            device.setName(deviceByProject.getName());
                            device.setTenantId(tenantId);
                            device.setCreateTime(new Date());
                            if (deviceByProject.getState() == 1) {

                                device.setOnlineState(1);
                            } else {
                                device.setOnlineState(0);
                            }
                            LinkappUser currentAmdin = linkappUserService.getCurrentTenantAdmin();
                            if (currentAmdin != null) {
                                device.setCreator(currentAmdin.getId().toString());
                            }
//                            LOGGER.info("getDevicesByProject save:" + JSON.toJSONString(device));
                            if (checkNotNullField(device.getCode(), device.getName(), device.getDeviceUnitId())) {
                                errorDeviceList.add(device);
                                LOGGER.error("设备数据同步not null字段存在null值，设备信息：" + JSON.toJSONString(device));
                                continue;
                            }
                            // 设备列表中没有该设备，同步关联至该项目的空间管理的第一个空间，创建时间最早
                            setDefaultArea(device,currentAmdin.getTenantId().toString());
                            deviceService.save(device);
                            noFundDeviceSaveSucc = true;
                            //添加的设备列表
                            addDeviceList.add(device);
                        }
                    }
                    if (!noFundDeviceSaveSucc) {
                        device = new Device();
                        device.setDeviceUnitCode(deviceByProject.getDeviceUnit().getCode());
                        device.setCode(deviceByProject.getDevice_id());
                        device.setName(deviceByProject.getName());
                        errorDeviceList.add(device);
                        LOGGER.info("不满足同步设备保存要求，型号或型号版本号为空，或找不到相应型号，deviceByProject对象是：{}", JSONObject.toJSONString(deviceByProject));
                    }
                } else {
                    device = deviceList.get(0);
                    if (deviceByProject.getDeviceUnit() != null && deviceByProject.getDeviceUnit().getDeviceVersion() != null) {
                        com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit deviceUnit = new com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit();
                        deviceUnit.setCode(deviceByProject.getDeviceUnit().getCode());
                        deviceUnit.setVersion(deviceByProject.getDeviceUnit().getDeviceVersion());
                        List<com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit> list = deviceUnitService.getAll(deviceUnit);
                        if (!list.isEmpty()) {
                            Device oldDevice = new Device(device.getLinkthingDelete(), device.getOnlineState(), device.getCode(), device.getName(), device.getDeviceUnitId(), device.getTenantId());
                            device.setDeviceUnitId(list.get(0).getId());
                            device.setCode(deviceByProject.getDevice_id());
                            device.setName(deviceByProject.getName());
                            if (!device.getTenantId().equalsIgnoreCase(tenantId)) {
                                //更换租户后,删除业务数据
                                UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
                                updateWrapper.eq("id", device.getId());
                                updateWrapper.set("modify_time", new Date());
                                updateWrapper.set("delete_state", 0);
                                deviceService.update(updateWrapper);
                                device.setDeviceModelRemoveStatus(1);// 设备所有模块标记为已移除
                                deviceService.changeDeviceModelRemoveStatus(device);
                                // 并新增新租户设备
                                Device newDevice = new Device();
                                newDevice.setDeviceUnitId(list.get(0).getId());
                                newDevice.setCode(deviceByProject.getDevice_id());
                                newDevice.setName(deviceByProject.getName());
                                newDevice.setTenantId(tenantId);
                                newDevice.setCreateTime(new Date());
                                if (deviceByProject.getState() == 1) {

                                    newDevice.setOnlineState(1);
                                } else {
                                    newDevice.setOnlineState(0);
                                }
                                LinkappUser currentAmdin = linkappUserService.getCurrentTenantAdmin();
                                if (currentAmdin != null) {
                                    newDevice.setCreator(currentAmdin.getId().toString());
                                }
//                            LOGGER.info("getDevicesByProject save:" + JSON.toJSONString(device));
                                if (checkNotNullField(newDevice.getCode(), newDevice.getName(), newDevice.getDeviceUnitId())) {
                                    errorDeviceList.add(newDevice);
                                    LOGGER.error("设备数据同步not null字段存在null值，设备信息：" + JSON.toJSONString(newDevice));
                                    continue;
                                }
                                // 设备列表中没有该设备，同步关联至该项目的空间管理的第一个空间，创建时间最早
                                setDefaultArea(newDevice,currentAmdin.getTenantId().toString());
                                deviceService.save(newDevice);
                                //添加的设备列表
                                addDeviceList.add(newDevice);
                            }else {
                                //未更换租户，更新设备信息
                                device.setTenantId(tenantId);
                                device.setLinkthingDelete(1);
                                if (deviceByProject.getState() == 1) {
                                    device.setOnlineState(1);
                                } else {
                                    device.setOnlineState(0);
                                }
//                            device.setModifyTime(new Date());
//                            LinkappUser currentAmdin = linkappUserService.getCurrentTenantAdmin();
//                            if (currentAmdin != null) {
//                                device.setModifier(currentAmdin.getId().toString());
//                            }
                                if (!oldDevice.equalsLinkthingsDevice(device)) {
                                    deviceService.updateById(device);
                                }
//                            挪到了设备型号 同步后更新
//                            synchronizaDistributioncabinet(tenantId, list.get(0).getCode(), list.get(0).getVersion());

                                //更新的设备列表
                                updateDeviceList.add(device);
                            }

                        } else {
                            //无法找到型号的设备
                            device = new Device();
                            device.setDeviceUnitCode(deviceByProject.getDeviceUnit().getCode());
                            device.setCode(deviceByProject.getDevice_id());
                            device.setName(deviceByProject.getName());
                            errorDeviceList.add(device);
                            LOGGER.info("不满足同步设备保存要求，更新设备，型号或型号版本号为空，deviceByProject对象是：{}", JSONObject.toJSONString(deviceByProject));
                        }
                    }
                }
            }

        }

        List<Device> deleteDeviceList = new ArrayList<>();

        deviceMap.forEach((k, v) -> {
            deleteDeviceList.add(v);
        });

        for (Device delDevice : deleteDeviceList) {
            UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id", delDevice.getId());
            updateWrapper.set("modify_time", new Date());
            updateWrapper.set("area_id", "");
            updateWrapper.set("latitude", "");
            updateWrapper.set("longitude", "");
            updateWrapper.set("site", "");
            updateWrapper.set("indoor_location", "");
            updateWrapper.set("area_path", "");
            if (deviceService.checkDeviceBeUsedByOtherBusiness(delDevice.getId())) {
                updateWrapper.set("delete_state", 0);
                deviceService.update(updateWrapper);
                delDevice.setDeviceModelRemoveStatus(1);// 设备所有模块标记为已移除
                deviceService.changeDeviceModelRemoveStatus(delDevice);
            } else {
                updateWrapper.set("linkthing_delete", 0);
                updateWrapper.set("online_state", 0);
                deviceService.update(updateWrapper);
            }
        }

        return map;
    }

    /**
     * 设置默认空间
     * @param device
     */
    private void setDefaultArea(Device device,String tenantId) {
        // 1.获取租户所有空间区域
        LinkappArea linkappArea = new LinkappArea();
        linkappArea.setTenantId(tenantId);
        List<SpaceTreeVo> areaList = linkappAreaMapper.selectLinkappAreaTreeList(linkappArea);
        if(CollectionUtil.isNotEmpty(areaList)){
            // areaList 按时间正序排列
            areaList.sort(Comparator.comparing(SpaceTreeVo::getCreateTime));
            // 2.获取创建时间最早的空间
            SpaceTreeVo area = areaList.get(0);
            // 3.将设备添加到该空间
            device.setSpaceId(area.getId());
            device.setAreaId(area.getId());
            device.setAreaPath(area.getAreaPath());
        }
    }

    /**
     * 验证非空字段，只要有一个是null就返回true;
     *
     * @param objs
     * @return
     */
    private Boolean checkNotNullField(Object... objs) {
        if (objs != null && objs.length > 0) {
            for (int i = 0, size = objs.length; i < size; i++) {
                if (ObjectUtils.isEmpty(objs[i])) {
                    return true;
                }
            }
        }
        return false;
    }


    @Transactional(rollbackFor = Exception.class)
    public RestMessage synchronizeDeleteDeviceUnitAttributeServiceParamMultiVersion(List<com.easylinkin.linkappapi.openapi.dto.DeviceUnitDTO> deviceUnitDTOList) {
        RestMessage restMessage = RestBuilders.successBuilder().build();
        if (ObjectUtils.isEmpty(deviceUnitDTOList)) {
            return restMessage;
        }
//        LOGGER.info("synchronizeDeleteDeviceUnitAttributeServiceParamMultiVersion be called 接口被调用,参数是:{}", JSON.toJSONString(deviceUnitDTOList));
        Set<String> deviceUnitIdSet = new HashSet<>();
        for (com.easylinkin.linkappapi.openapi.dto.DeviceUnitDTO deviceUnitDTO : deviceUnitDTOList) {
            if (ObjectUtils.isEmpty(deviceUnitDTO.getDeviceVersions())) {
                continue;
            }
            for (DeviceVersion deviceVersion : deviceUnitDTO.getDeviceVersions()) {
                DeviceUnit deviceUnit = new DeviceUnit().setCode(deviceUnitDTO.getDeviceUnitCode()).setVersion(deviceVersion.getDeviceVersion());
                List<DeviceUnit> deviceUnitList = deviceUnitService.getAll(deviceUnit);
                // 查询不存在，则跳过
                boolean isNewVersionDeviceUnit = deviceUnitList.isEmpty();
                if (isNewVersionDeviceUnit) {
                    continue;
                }
                deviceUnit = deviceUnitList.get(0);
                //查询型号是否正在使用
                QueryWrapper<TenantRefDeviceUnit> tenantRefDeviceUnitQueryWrapper = new QueryWrapper<>();
                tenantRefDeviceUnitQueryWrapper.eq("device_unit_id", deviceUnit.getId());
                List<TenantRefDeviceUnit> tenantRefDeviceUnits = tenantRefDeviceUnitMapper.selectList(tenantRefDeviceUnitQueryWrapper);
                if (tenantRefDeviceUnits != null && tenantRefDeviceUnits.size() > 0) {
                    Set<String> collect = tenantRefDeviceUnits.stream().map(m -> m.getTenantId()).collect(Collectors.toSet());
                    QueryWrapper<LinkappUser> linkappUserQueryWrapper = new QueryWrapper<>();
                    linkappUserQueryWrapper.in("tenant_id", collect);
                    linkappUserQueryWrapper.eq("type", "1");
                    linkappUserQueryWrapper.select("username");
                    List<LinkappUser> list = linkappUserMapper.selectList(linkappUserQueryWrapper);
                    Set<String> tenantNames = list.stream().map(m -> m.getUsername()).collect(Collectors.toSet());
                    String accountDesc = "正在使用中";
                    if (tenantNames != null && tenantNames.size() > 0) {
                        accountDesc = "账号" + tenantNames.toString() + accountDesc;
                    }
                    throw new IllegalArgumentException("设备型号:" + deviceUnit.getCode() + ",版本:" + deviceUnit.getVersion() + "," + accountDesc);
                }
                //添加集合，等待删除
                deviceUnitIdSet.add(deviceUnit.getId());
            }
        }

        if (deviceUnitIdSet == null || deviceUnitIdSet.size() <= 0) {
            return restMessage;
        }
        //执行删除

        //查询删除设备服务相关
        QueryWrapper<DeviceServices> qwDeviceService = new QueryWrapper<>();
        qwDeviceService.in("device_unit_id", deviceUnitIdSet);
        List<DeviceServices> deviceServicesList = deviceServiceService.list(qwDeviceService);
        if (deviceServicesList != null && deviceServicesList.size() > 0) {
            Set<String> collect = deviceServicesList.stream().map(m -> m.getId()).collect(Collectors.toSet());
            //根据设备服务删除-设备服务参数
            QueryWrapper<DeviceParm> qwDeviceParm = new QueryWrapper<>();
            qwDeviceParm.in("device_service_id", collect);
            deviceParmService.remove(qwDeviceParm);
            //删除设备服务租户配置
            QueryWrapper<DeviceServiceTenantConfig> deviceServiceTenantConfigQueryWrapper = new QueryWrapper<>();
            deviceServiceTenantConfigQueryWrapper.in("device_service_id", collect);
            deviceServiceTenantConfigService.remove(deviceServiceTenantConfigQueryWrapper);
            //删除设备服务
            deviceServiceService.removeByIds(collect);
        }

        //删除设备型号属性相关
        QueryWrapper<DeviceAttribute> deviceAttributeQueryWrapper = new QueryWrapper<>();
        deviceAttributeQueryWrapper.in("device_unit_id", deviceUnitIdSet);
        List<DeviceAttribute> deviceAttributeList = deviceAttributeService.list(deviceAttributeQueryWrapper);
        if (deviceAttributeList != null && deviceAttributeList.size() > 0) {
            Set<String> collect = deviceAttributeList.stream().map(m -> m.getId()).collect(Collectors.toSet());
            //根据属性，删除租户配置
            QueryWrapper<DeviceAttributeTenantConfig> deviceAttributeTenantConfigQueryWrapper = new QueryWrapper<>();
            deviceAttributeTenantConfigQueryWrapper.in("device_attribute_id", collect);
            deviceAttributeTenantConfigService.remove(deviceAttributeTenantConfigQueryWrapper);
            //删除属性
            deviceAttributeService.removeByIds(collect);
        }

        //删除型号
        deviceUnitService.removeByIds(deviceUnitIdSet);

        return restMessage;
    }
}
